Completed
Push — master ( c39f44...cd1c07 )
by Sander
01:08
created

background.constructor   B

Complexity

Conditions 1
Paths 2

Size

Total Lines 593

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 1
c 4
b 0
f 0
nc 2
nop 0
dl 0
loc 593
rs 8.2857

57 Functions

Rating   Name   Duplication   Size   Complexity  
A background.js ➔ isMasterPasswordValid 0 9 2
A background.js ➔ setMasterPassword 0 17 4
A background.js ➔ getMasterPasswordSet 0 3 1
A API.runtime.onConnect.addListener 0 10 1
A API.runtime.onConnect.addListener 0 6 2
B background.js ➔ getCredentialsByUrl 0 18 9
A background.js ➔ getRuntimeSettings 0 3 1
A background.js ➔ getSettings 0 52 1
A background.js ➔ ... ➔ storage.then 0 3 1
A background.js ➔ ... ➔ setInterval 0 3 1
C background.js ➔ ... ➔ PAPI.getVault 0 28 7
A background.js ➔ getSetting 0 3 1
B background.js ➔ getCredentials 0 32 1
C background.js ➔ ... ➔ storage.then 0 49 12
B background.js ➔ saveSettings 0 27 4
A background.js ➔ passToParent 0 4 1
A background.js ➔ ... ➔ PAPI.updateCredential 0 7 2
B background.js ➔ searchCredential 0 15 5
A storage.then 0 9 2
A background.js ➔ saveCredential 0 21 4
A background.js ➔ getCredentialByGuid 0 8 3
A background.js ➔ ... ➔ PAPI.updateCredential 0 5 2
A background.js ➔ ... ➔ API.tabs.then 0 4 1
A background.js ➔ updateCredentialUrlDoorhanger 0 12 1
A API.tabs.onActivated.addListener 0 7 2
A background.js ➔ saveMinedCallback 0 7 1
A storage.error 0 5 2
A background.js ➔ updateCredentialUrl 0 5 1
A background.js ➔ getCredentialForHTTPAuth 0 3 1
A background.js ➔ isVaultKeySet 0 3 1
A background.js ➔ displayLogoutIcons 0 21 2
B background.js ➔ minedForm 0 30 4
A background.js ➔ ... ➔ PAPI.createCredential 0 5 1
A background.js ➔ updateTabsIcon 0 8 1
B background.js ➔ createIconForTab 0 25 4
A background.js ➔ ... ➔ PAPI.createCredential 0 5 1
A background.js ➔ ... ➔ PAPI.createCredential 0 3 1
A background.js ➔ getMinedData 0 13 2
A background.js ➔ ignoreSite 0 10 3
B background.js ➔ saveMined 0 37 5
A API.runtime.onMessage.addListener 0 14 4
A background.js ➔ ... ➔ API.tabs.then 0 2 1
A background.js ➔ ... ➔ API.tabs.then 0 10 1
A API.tabs.onActivated.addListener 0 9 1
A background.js ➔ ... ➔ API.tabs.then 0 2 1
A background.js ➔ clearMined 0 3 1
A background.js ➔ injectCreateCredential 0 13 1
A background.js ➔ getDoorhangerData 0 3 1
A background.js ➔ ... ➔ API.tabs.then 0 5 1
A background.js ➔ isAutoFillEnabled 0 6 2
A background.js ➔ ... ➔ _self.settings.ignored_sites.filter 0 3 1
A background.js ➔ getActiveTab 0 7 1
A API.tabs.onUpdated.addListener 0 7 2
A background.js ➔ ... ➔ API.tabs.then 0 17 2
A background.js ➔ ... ➔ API.tabs.then 0 6 2
A background.js ➔ setDoorhangerData 0 3 1
A background.js ➔ ... ➔ API.tabs.then 0 2 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
10
    API.runtime.onConnect.addListener(function (port) {
11
12
        port.onMessage.addListener(function (msg) {
13
            if (msg === 'credential_amount') {
14
                port.postMessage('credential_amount:' + local_credentials.length);
15
            }
16
17
        });
18
19
    });
20
21
    var master_password = null;
22
23
    function getMasterPasswordSet() {
24
        return (master_password !== null);
25
    }
26
27
    _self.getMasterPasswordSet = getMasterPasswordSet;
28
29
    function setMasterPassword(opts) {
30
        master_password = opts.password;
31
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
32
            // Save the password in plain text on user request.
33
            // No secure local storage is available :/
34
            storage.set('master_password', opts.password);
35
        } else {
36
            storage.set('master_password', null);
37
        }
38
39
        if (opts.password) {
40
            getSettings();
41
        } else {
42
            displayLogoutIcons();
43
        }
44
45
    }
46
47
    _self.setMasterPassword = setMasterPassword;
48
49
50
    var testMasterPasswordAgainst;
51
52
    function isMasterPasswordValid(password) {
53
        //return true;
54
        try {
55
            PAPI.decryptString(testMasterPasswordAgainst, password);
56
            return true;
57
        } catch (e) {
58
            return false;
59
        }
60
    }
61
62
    _self.isMasterPasswordValid = isMasterPasswordValid;
63
64
65
    var local_credentials = [];
66
    var local_vault = [];
67
    var encryptedFieldSettings = ['default_vault', 'nextcloud_host', 'nextcloud_username', 'nextcloud_password', 'vault_password'];
68
    _self.settings = {};
69
    _self.ticker = null;
70
    _self.running = false;
71
    function getSettings() {
72
73
        storage.get('settings').then(function (_settings) {
74
75
            if (!_settings || !_settings.hasOwnProperty('nextcloud_host')) {
76
                return;
77
            }
78
79
            if (!master_password && _settings.hasOwnProperty('nextcloud_username') && _settings.hasOwnProperty('vault_password')) {
80
                _self.settings.isInstalled = 1;
81
                testMasterPasswordAgainst = _settings.nextcloud_username;
82
                return;
83
            }
84
85
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
86
                var field = encryptedFieldSettings[i];
87
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
88
            }
89
90
91
            _self.settings = _settings;
92
93
            if(!_self.settings.hasOwnProperty('ignored_sites')){
94
                _self.settings.ignored_sites = [];
95
            }
96
97
            if (!_self.settings.hasOwnProperty('disable_browser_autofill')) {
98
                _self.settings.disable_browser_autofill = true;
99
            }
100
101
102
            PAPI.host = _settings.nextcloud_host;
103
            PAPI.username = _settings.nextcloud_username;
104
            PAPI.password = _settings.nextcloud_password;
105
            if (!_settings.vault_password) {
106
                return;
107
            }
108
            if (PAPI.credentialsSet()) {
109
                getCredentials();
110
                if (_self.running) {
111
                    clearInterval(_self.ticker);
112
                }
113
114
                _self.running = true;
115
                _self.ticker = setInterval(function () {
116
                    getCredentials();
117
                }, _self.settings.refreshTime * 1000);
118
            } else {
119
                console.log('Login details are missing!');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
120
            }
121
        });
122
    }
123
124
    _self.getSettings = getSettings;
125
126
    function getRuntimeSettings() {
127
        return _self.settings;
128
    }
129
130
    _self.getRuntimeSettings = getRuntimeSettings;
131
132
    function getSetting(name) {
133
        return _self.settings[name];
134
    }
135
136
    _self.getSetting = getSetting;
137
138
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
139
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
140
            var field = encryptedFieldSettings[i];
141
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
142
        }
143
        PAPI.host = settings.nextcloud_host;
144
        PAPI.username = settings.nextcloud_username;
145
        PAPI.password = settings.nextcloud_password;
146
147
        if(!settings.hasOwnProperty('ignored_sites')){
148
            settings.ignored_sites = [];
149
        }
150
151
        if (!settings.hasOwnProperty('disable_browser_autofill')) {
152
            settings.disable_browser_autofill = true;
153
        }
154
155
        //window.settings contains the run-time settings
156
        _self.settings = settings;
157
158
159
160
        storage.set('settings', settings).then(function () {
161
            getSettings();
162
        });
163
164
    }
165
166
    _self.saveSettings = saveSettings;
167
168
169
    function getCredentials() {
170
        //console.log('Loading vault with the following settings: ', settings);
171
        var tmpList = [];
172
        PAPI.getVault(_self.settings.default_vault.guid, function (vault) {
173
            if (vault.hasOwnProperty('error')) {
174
                return;
175
            }
176
            var _credentials = vault.credentials;
177
            for (var i = 0; i < _credentials.length; i++) {
178
                var key = _self.settings.vault_password;
179
                var credential = _credentials[i];
180
                if (credential.hidden === 1) {
181
                    continue;
182
                }
183
                var usedKey = key;
184
                //Shared credentials are not implemented yet
185
                if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
186
                    usedKey = PAPI.decryptString(credential.shared_key, key);
187
188
                }
189
                credential = PAPI.decryptCredential(credential, usedKey);
190
                if (credential.delete_time === 0) {
191
                    tmpList.push(credential);
192
                }
193
194
            }
195
            delete vault.credentials;
196
            local_vault = vault;
197
            local_credentials = tmpList;
198
            updateTabsIcon();
199
        });
200
    }
201
202
    _self.getCredentials = getCredentials;
203
204
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
205
        if (!master_password) {
206
            return [];
207
        }
208
        if (!_url || _url === '') {
209
            return [];
210
        }
211
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
212
        var found_list = [];
213
        for (var i = 0; i < local_credentials.length; i++) {
214
            if (local_credentials[i].url && local_credentials[i].username && local_credentials[i].password) {
215
                if (local_credentials[i].url.indexOf(url) !== -1) {
216
                    found_list.push(local_credentials[i]);
217
                }
218
            }
219
        }
220
        return found_list;
221
    }
222
223
    _self.getCredentialsByUrl = getCredentialsByUrl;
224
225
226
    function saveCredential(credential) {
227
        if (!credential.credential_id) {
228
            PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
229
                local_credentials.push(createdCredential);
230
            });
231
        } else {
232
            var credential_index;
233
            for (var i = 0; i < local_credentials.length; i++) {
234
                if (local_credentials[i].guid === credential.guid) {
235
                    credential_index = i;
236
                    break;
237
                }
238
            }
239
240
            PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
241
                if (credential_index) {
242
                    local_credentials[credential_index] = updatedCredential;
243
                }
244
            });
245
        }
246
    }
247
248
    _self.saveCredential = saveCredential;
249
250
    function getCredentialByGuid(guid) {
251
        for (var i = 0; i < local_credentials.length; i++) {
252
            var credential = local_credentials[i];
253
            if (credential.guid === guid) {
254
                return credential;
255
            }
256
        }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
257
    }
258
259
    _self.getCredentialByGuid = getCredentialByGuid;
260
261
    function getCredentialForHTTPAuth(req) {
262
        return getCredentialsByUrl(req.url)[0];
263
    }
264
265
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
266
267
    var mined_data = [];
268
269
    function minedForm(data, sender) {
270
        var url = sender.url;
271
        var existingLogins = getCredentialsByUrl(sender.url);
272
        var title = API.i18n.getMessage('detected_new_login') + ':';
273
        var minedMatchingID = null;
274
        for (var j = 0; j < existingLogins.length; j++) {
275
            var login = existingLogins[j];
276
            if (login.username === data.username) {
277
                if (login.password !== data.password) {
278
                    minedMatchingID = login.guid;
279
                    title = API.i18n.getMessage('detected_changed_login') + ':';
280
                }
281
                else {
282
                    //console.log('No changes detected');
283
                    delete mined_data[sender.tab.id];
284
                    return;
285
                }
286
            }
287
        }
288
        mined_data[sender.tab.id] = {
289
            title: title,
290
            url: url,
291
            username: data.username,
292
            password: data.password,
293
            label: sender.title,
294
            guid: minedMatchingID
295
        };
296
297
        //console.log('Done mining, ', mined_data, sender.tab.id);
298
    }
299
300
    _self.minedForm = minedForm;
301
302
    function getMinedData(args, sender) {
303
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
304
        var senderUrl = sender.tab.url;
305
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
306
        var matches = _self.settings.ignored_sites.filter(function (item) {
307
            return typeof item === 'string' && site.indexOf(item) > -1;
308
        });
309
310
        if(matches.length !== 0){
311
            return null;
312
        }
313
        return mined_data[sender.tab.id];
314
    }
315
316
    _self.getMinedData = getMinedData;
317
318
    function clearMined(args, sender) {
319
        delete mined_data[sender.tab.id];
320
    }
321
322
    _self.clearMined = clearMined;
323
324
    function saveMinedCallback(args) {
325
        createIconForTab(args.sender.tab);
326
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
327
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
328
            });
329
        });
330
    }
331
332
    function ignoreSite(_url) {
333
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
334
            _self.settings.ignored_sites = [];
335
        }
336
        var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
337
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
338
            _self.settings.ignored_sites.push(site);
339
            saveSettings(_self.settings);
340
        }
341
    }
342
343
    _self.ignoreSite = ignoreSite;
344
345
    function passToParent(args, sender) {
346
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
347
        });
348
    }
349
350
    _self.passToParent = passToParent;
351
352
    function getActiveTab(opt) {
353
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
354
            var tab = tabs[0];
355
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
356
            });
357
        });
358
    }
359
360
    _self.getActiveTab = getActiveTab;
361
362
    function updateCredentialUrlDoorhanger(login) {
363
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
364
            var tab = tabs[0];
365
            var data = login;
366
            data.url = tab.url;
367
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
368
            API.tabs.sendMessage(tab.id, {
369
                method: 'showUrlUpdateDoorhanger',
370
                args: {data: data}
371
            });
372
        });
373
    }
374
375
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
376
377
    function updateCredentialUrl(data, sender) {
378
        mined_data[sender.tab.id] = data;
379
        saveMined({}, sender);
380
381
    }
382
383
    _self.updateCredentialUrl = updateCredentialUrl;
384
385
    function saveMined(args, sender) {
386
        var data = mined_data[sender.tab.id];
387
        var credential,
388
            credential_index;
389
390
        if (data.guid === null) {
391
            credential = PAPI.newCredential();
392
        } else {
393
            for (var i = 0; i < local_credentials.length; i++) {
394
                if (local_credentials[i].guid === data.guid) {
395
                    credential = local_credentials[i];
396
                    credential_index = i;
397
                    break;
398
                }
399
            }
400
        }
401
        credential.username = data.username;
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
402
        credential.password = data.password;
403
        credential.url = sender.tab.url;
404
        if (credential.guid !== null) {
405
            PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
406
                if (credential_index) {
407
                    local_credentials[credential_index] = updatedCredential;
408
                }
409
                saveMinedCallback({credential: credential, updated: true, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
410
                delete mined_data[sender.tab.id];
411
            });
412
        } else {
413
            credential.label = sender.tab.title;
414
            credential.vault_id = local_vault.vault_id;
415
            PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
416
                saveMinedCallback({credential: credential, updated: false, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
417
                local_credentials.push(createdCredential);
418
                delete mined_data[sender.tab.id];
419
            });
420
        }
421
    }
422
423
    _self.saveMined = saveMined;
424
425
    function searchCredential(searchText) {
426
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
427
        var results = [];
428
        for (var i = 0; i < local_credentials.length; i++) {
429
            var credential = local_credentials[i];
430
            for (var f = 0; f < searchFields.length; f++) {
431
                var field = searchFields[f];
432
                if (credential[field] && credential[field].indexOf(searchText) !== -1) {
433
                    results.push(credential);
434
                    break;
435
                }
436
            }
437
        }
438
        return results;
439
    }
440
441
    _self.searchCredential = searchCredential;
442
443
444
    function injectCreateCredential(args, sender) {
445
        var credential = PAPI.newCredential();
446
        credential.label = args.label;
447
        credential.username = args.username;
448
        credential.password = args.password;
449
        credential.vault_id = local_vault.vault_id;
450
        credential.url = sender.tab.url;
451
        PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
452
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
453
            local_credentials.push(createdCredential);
454
455
        });
456
    }
457
458
    self.injectCreateCredential = injectCreateCredential;
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
459
460
    function isVaultKeySet() {
461
        return (_self.settings.vault_password !== null);
462
    }
463
464
    _self.isVaultKeySet = isVaultKeySet;
465
466
    function isAutoFillEnabled() {
467
        if (!_self.settings.hasOwnProperty('disableAutoFill')) {
468
            return true;
469
        }
470
        return (_self.settings.disableAutoFill === false);
471
    }
472
473
    _self.isAutoFillEnabled = isAutoFillEnabled;
474
475
    var doorhangerData = null;
476
    function setDoorhangerData(data) {
477
        doorhangerData = data;
478
    }
479
    _self.setDoorhangerData = setDoorhangerData;
480
481
    function getDoorhangerData() {
482
        return doorhangerData;
483
    }
484
    _self.getDoorhangerData = getDoorhangerData;
485
486
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
487
488
        if (!msg || !msg.hasOwnProperty('method')) {
489
            return;
490
        }
491
        var result = false;
492
        if (_self[msg.method]) {
493
            result = _self[msg.method](msg.args, sender);
494
        } else {
495
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
496
        }
497
498
        sendResponse(result);
499
    });
500
501
    var defaultColor = '#0082c9';
502
503
    function createIconForTab(tab) {
504
        if (!master_password) {
505
            return;
506
        }
507
        var tabUrl = tab.url;
508
        var logins = getCredentialsByUrl(tabUrl);
509
        if (tab.active) {
510
            window.contextMenu.setContextItems(logins);
511
        }
512
        var credentialAmount = logins.length;
513
        API.browserAction.setBadgeText({
514
            text: credentialAmount.toString(),
515
            tabId: tab.id
516
        });
517
        API.browserAction.setBadgeBackgroundColor({
518
            color: defaultColor,
519
            tabId: tab.id
520
        });
521
522
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
523
        API.browserAction.setTitle({
524
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
525
            tabId: tab.id
526
        });
527
    }
528
529
    function displayLogoutIcons() {
530
        if (_self.settings) {
531
            API.tabs.query({}).then(function (tabs) {
532
                for (var t = 0; t < tabs.length; t++) {
533
                    var tab = tabs[t];
534
                    API.browserAction.setBadgeText({
535
                        text: '🔑',
536
                        tabId: tab.id
537
                    });
538
                    API.browserAction.setBadgeBackgroundColor({
539
                        color: '#ff0000',
540
                        tabId: tab.id
541
                    });
542
                    API.browserAction.setTitle({
543
                        title: API.i18n.getMessage('browser_action_title_locked'),
544
                        tabId: tab.id
545
                    });
546
                }
547
            });
548
        }
549
    }
550
551
    function updateTabsIcon() {
552
        API.tabs.query({}).then(function (tabs) {
553
            for (var t = 0; t < tabs.length; t++) {
554
                var tab = tabs[t];
555
                createIconForTab(tab);
556
            }
557
        });
558
    }
559
560
561
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
562
        if (master_password) {
563
            createIconForTab(tab);
564
        } else {
565
            displayLogoutIcons();
566
        }
567
    });
568
569
    API.tabs.onActivated.addListener(function () {
570
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
571
            if (master_password) {
572
                createIconForTab(tabs[0]);
573
            } else {
574
                displayLogoutIcons();
575
            }
576
        });
577
    });
578
579
    displayLogoutIcons();
580
581
    storage.get('master_password').then(function (password) {
582
        if (password) {
583
            master_password = password;
584
            API.api.browserAction.setBadgeBackgroundColor({
585
                color: defaultColor
586
            });
587
        }
588
        getSettings();
589
    }).error(function (error) {
590
        if (error === "Data not found") {
591
            getSettings();
592
        }
593
    });
594
    return _window;
595
}());
596
597